export const configWithPath = (fn: any) => {
  return (...fnArg: any) => {
    const config = fn(...fnArg);
    const pathSet: any = {};

    const namePathFind = (fields: any[]) => {
      return fields.map((field: any) => {
        const newField = { ...field };

        if (newField.namePath) {
          pathSet[newField.name] = {
            key: newField.namePath,
            type: 'namePath',
          };
          delete newField.namePath;
        }
        if (newField.name && newField.name.includes('.')) {
          pathSet[newField.name] = {
            key: newField.name.substring(0, newField.name.lastIndexOf('.')),
            type: 'name',
            realName: newField.name.substring(newField.name.lastIndexOf('.') + 1),
          };
        }
        if (newField.fields) {
          newField.fields = namePathFind(newField.fields);
        }
        return newField;
      });
    };

    config.fields = namePathFind(config.fields);
    const oldConvertParams = config.submit.convertParams;
    config.submit.convertParams = ({ ...arg }: any) => {
      const newArg = { ...arg };
      const newParams = newArg.params;
      Object.keys(pathSet).forEach((setKey: any) => {
        const { key, type, realName } = pathSet[setKey];
        const paths = key.split('.');
        const realValue = newParams[setKey];
        delete newParams[setKey];
        let thisObj: any;
        paths.forEach((pathKey: string, index: number) => {
          if (index === 0) {
            thisObj = newParams;
          }
          if (!thisObj[pathKey]) {
            thisObj[pathKey] = {};
          }
          if (index === paths.length - 1) {
            let lastKey = setKey;
            if (type === 'name') {
              lastKey = realName;
            }
            thisObj[pathKey][lastKey] = realValue || undefined;
          }
          thisObj = thisObj[pathKey];
        });
      });
      newArg.params = newParams;
      if (oldConvertParams) {
        return oldConvertParams(newArg);
      }
      return newArg;
    };

    const oldRemoteValuesConvert = config.remoteValues.converter;
    config.remoteValues.converter = ({ ...arg }: any) => {
      const newArg = { ...arg };
      const newParams = newArg.data;
      Object.keys(pathSet).forEach((setKey: any) => {
        const { key, type, realName } = pathSet[setKey];
        const paths = key.split('.');
        let realValue = newParams;
        paths.forEach((pathKey: string) => {
          realValue = realValue[pathKey];
        });
        let lastKey = setKey;
        if (type === 'name') {
          lastKey = realName;
        }
        newParams[setKey] = realValue[lastKey];
      });
      newArg.data = newParams;
      if (oldRemoteValuesConvert) {
        return oldRemoteValuesConvert(newArg);
      }
      return newArg;
    };

    return config;
  };
};

type setValueFn = (initValue: any) => any;
type setValueFnItem = {
  initFn: setValueFn;
  key: string | string[];
};

export const configDealWithValue = (fn: any) => {
  return (...fnArg: any) => {
    const config = fn(...fnArg);
    const valueInitSet: setValueFnItem[] = [];
    const valueSubmitSet: setValueFnItem[] = [];

    const namePathFind = (fields: any[]) => {
      return fields.map((field: any) => {
        const newField = { ...field };

        if (newField.rValueInit) {
          valueInitSet.push({
            key: newField.name,
            initFn: newField.rValueInit,
          });
          delete newField.rValueInit;
        }
        if (newField.rValueSubmit) {
          valueSubmitSet.push({
            key: newField.name,
            initFn: newField.rValueSubmit,
          });
          delete newField.rValueSubmit;
        }

        if (newField.fields) {
          newField.fields = namePathFind(newField.fields);
        }
        return newField;
      });
    };
    config.fields = namePathFind(config.fields);

    if (!config.remoteValues) {
      config.remoteValues = {};
    }
    if (!config.submit) {
      config.submit = {};
    }
    const oldConvertParams = config.submit.convertParams;
    config.submit.convertParams = ({ ...arg }: any) => {
      const newArg = { ...arg };
      const newParams = newArg.params;

      valueSubmitSet.forEach(({ key, initFn }) => {
        if (key instanceof Array) {
          let realValue = newParams;
          let lastObj = {};
          let lastKey = '';
          key.forEach((point) => {
            lastObj = realValue;
            lastKey = point;
            realValue = realValue && realValue[point];
          });
          if (lastObj) {
            lastObj[lastKey] = initFn(realValue);
          }
        } else {
          newParams[key] = initFn(newParams[key]);
        }
      });

      newArg.params = newParams;
      if (oldConvertParams) {
        return oldConvertParams(newArg);
      }
      console.log();

      return newArg;
    };

    const oldRemoteValuesConvert = config.remoteValues.converter;
    config.remoteValues.converter = ({ ...arg }: any) => {
      const newArg = { ...arg };
      const newParams = newArg.data;
      valueInitSet.forEach(({ key, initFn }) => {
        if (key instanceof Array) {
          let realValue = newParams;
          let lastObj = {};
          let lastKey = '';
          key.forEach((point) => {
            lastObj = realValue;
            lastKey = point;
            realValue = realValue && realValue[point];
          });
          if (lastObj) {
            lastObj[lastKey] = initFn(realValue);
          }
        } else {
          newParams[key] = initFn(newParams[key]);
        }
      });

      newArg.data = newParams;
      if (oldRemoteValuesConvert) {
        return oldRemoteValuesConvert(newArg);
      }
      return newArg;
    };

    return config;
  };
};

// 合并提交参数，把详情获取的所有参数跟修改的表单进行合并
export function mergeParams<T>(params: T, newParams: T): T {
  let returnParams: any = {};
  if (params instanceof Array && newParams instanceof Array) {
    if (params.length > newParams.length) {
      returnParams = params.map((item, index) => mergeParams(item, newParams[index]));
    } else {
      returnParams = newParams.map((item, index) => mergeParams(returnParams[index], item));
    }
  } else if (params instanceof Object && newParams instanceof Object) {
    const paramsKeys = Object.keys(params);
    const newParamsKeys = Object.keys(newParams);
    const allKeys = new Set([...paramsKeys, ...newParamsKeys]);
    allKeys.forEach((key) => {
      if (newParams[key] !== undefined) {
        if (params[key] instanceof Object && newParams[key] instanceof Object) {
          returnParams[key] = mergeParams(params[key], newParams[key]);
        } else {
          returnParams[key] = newParams[key];
        }
      } else {
        returnParams[key] = params[key];
      }
    });
  } else {
    returnParams = newParams;
  }
  return returnParams;
}
